package com.lagou.edu.spring.core.impl;

import com.lagou.edu.spring.core.ApplicationContext;
import com.lagou.edu.spring.core.BeanDefinition;
import com.lagou.edu.spring.exceptions.MySpringException;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;

import java.io.InputStream;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * xml文件配置容器
 *
 * @author 大犬
 * @date 2020/3/31
 */
public class XmlConfigApplicationContext extends AbstractApplicationContext implements ApplicationContext {

    public XmlConfigApplicationContext(String configFilePath) {
        // 任务一：读取解析xml，通过反射技术实例化对象并且存储待用（map集合）
        // 加载xml
        InputStream resourceAsStream = XmlConfigApplicationContext.class.getClassLoader().getResourceAsStream(configFilePath);
        try {
            parseConfigFile(resourceAsStream);
        } catch (Exception e) {
            throw new MySpringException("parse configFile failed", e);
        }

    }

    private void parseConfigFile(InputStream resourceAsStream) throws DocumentException, ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchFieldException {
        // 解析xml
        SAXReader saxReader = new SAXReader();
        Document document = saxReader.read(resourceAsStream);
        Element rootElement = document.getRootElement();
        // 根据配置文件中的所有bean标签注册bean定义信息
        registerBeans(rootElement);
    }

    private void registerBeans(Element rootElement) throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchFieldException {
        List<Element> beanList = rootElement.selectNodes("//bean");
        for (int i = 0; i < beanList.size(); i++) {
            Element element = beanList.get(i);
            // 处理每个bean元素，获取到该元素的id 和 class 属性
            String id = element.attributeValue("id");
            String clazz = element.attributeValue("class");
            // 通过反射技术获取类型
            Class<?> aClass = Class.forName(clazz);
            // 封装成beanDefinition对象
            BeanDefinition beanDefinition = new BeanDefinition();
            beanDefinition.setBeanName(id);
            beanDefinition.setBeanType(aClass);
            // 解析属性
            parseProperties(element, beanDefinition);

        }
    }

    private void parseProperties(Element element, BeanDefinition beanDefinition) throws NoSuchFieldException {
        List<Element> propertyElements = element.selectNodes("//property");
        Class beanType = beanDefinition.getBeanType();
        Map<String, Field> dependencies = new HashMap<>(propertyElements.size());
        for (int i = 0; i < propertyElements.size(); i++) {
            Element property = propertyElements.get(i);
            String name = property.attributeValue("name");
            String ref = property.attributeValue("ref");
            Field declaredField = beanType.getDeclaredField(name);
            dependencies.put(ref, declaredField);
        }
        beanDefinition.setPropertyDependencies(dependencies);
    }

}
